package com.dreamlike.loomdemo;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class ProxyGenerator {
    public static DynamicType.Builder.MethodDefinition copyMethod(Class target, ControllerInterceptor interceptor){
        DynamicType.Builder.MethodDefinition now = null;
        DynamicType.Builder<Object> builder = new ByteBuddy()
                .subclass(Object.class)
                .name(target.getName()+"$$loomEnhance")
                .annotateType(target.getAnnotations());
        final List<Method> methodList = Arrays.stream(target.getDeclaredMethods()).filter(m -> Modifier.isPublic(m.getModifiers())).collect(Collectors.toList());
        for (Method method : methodList) {
            TypeDescription.Generic returnType;
            if (method.getReturnType() == DeferredResult.class){
                returnType = TypeDefinition.Sort.describe(method.getGenericReturnType());
            }else {
               returnType = TypeDescription.Generic.Builder.parameterizedType(DeferredResult.class, method.getGenericReturnType()).build();
            }
            if (now == null) {
                now = copyParameters(builder.defineMethod(method.getName(), returnType, Modifier.PUBLIC), method)
                        .intercept(MethodDelegation.to(interceptor))
                        .annotateMethod(method.getAnnotations());
            }else {
                now = copyParameters(now.defineMethod(method.getName(), returnType, Modifier.PUBLIC), method)
                        .intercept(MethodDelegation.to(interceptor))
                        .annotateMethod(method.getAnnotations());
            }

        }
        return now;
    }
    private static DynamicType.Builder.MethodDefinition.ExceptionDefinition copyParameters(DynamicType.Builder.MethodDefinition.ParameterDefinition.Initial i, Method method){
        DynamicType.Builder.MethodDefinition.ParameterDefinition.Annotatable now = null;
        final Parameter[] parameters = method.getParameters();
        if (parameters.length == 0){
            return i;
        }
        for (Parameter parameter : parameters) {
           if (now == null){
               now = i.withParameter(parameter.getParameterizedType(), parameter.getName(), 0)
                       .annotateParameter(parameter.getAnnotations());
           }else {
               now = now.withParameter(parameter.getParameterizedType(), parameter.getName(), 0)
                       .annotateParameter(parameter.getAnnotations());
           }
        }
        return now;
    }

    @RestController
    private class Supply{}
}
